/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.test.mybatis.datascope.integration

import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ActiveProfiles
import tony.mybatis.datascope.IDataScopeContext
import tony.test.mybatis.datascope.DataScopeBaseTest
import tony.test.mybatis.datascope.TestPaginationInterceptor
import tony.test.mybatis.datascope.entity.SysDict
import tony.test.mybatis.datascope.entity.SysDictType
import tony.test.mybatis.datascope.mapper.SysDictMapper
import tony.test.mybatis.datascope.mapper.SysDictTypeMapper
import java.time.LocalDateTime

/**
 * 数据权限分页集成正确性测试
 * @author tangli
 * @date 2025/09/21 10:00
 */
@ActiveProfiles("mysql")
@DisplayName("数据权限分页集成正确性测试")
class DataScopePaginationIntegrationTest : DataScopeBaseTest() {

    @Autowired
    private lateinit var dataScopeContext: IDataScopeContext

    @Autowired
    private lateinit var testPaginationInterceptor: TestPaginationInterceptor

    @Autowired
    private lateinit var sysDictMapper: SysDictMapper

    @Autowired
    private lateinit var sysDictTypeMapper: SysDictTypeMapper

    @BeforeAll
    fun setUp() {
        testPaginationInterceptor.clearCapturedSqls()
        prepareTestData()
    }

    @AfterAll
    fun tearDown() {
        sysDictMapper.delete(null)
        sysDictTypeMapper.delete(null)
    }

    private fun prepareTestData() {
        logger.info("=== 准备测试数据 ===")

        // 插入字典类型数据
        val dictType1 = SysDictType().apply {
            dictTypeId = "type1"
            dictTypeName = "系统配置"
            dictTypeCode = "system_config"
            buildIn = true
            createTime = LocalDateTime.now()
            creatorId = "user1"
            creatorOrgId = "org1"
        }
        val dictType2 = SysDictType().apply {
            dictTypeId = "type2"
            dictTypeName = "业务配置"
            dictTypeCode = "business_config"
            buildIn = true
            createTime = LocalDateTime.now()
            creatorId = "user2"
            creatorOrgId = "org2"
        }
        sysDictTypeMapper.insert(dictType1)
        sysDictTypeMapper.insert(dictType2)

        // 插入字典数据 - user1的数据
        for (i in 1..15) {
            val dict = SysDict().apply {
                dictId = "dict_user1_$i"
                dictName = "字典名称$i"
                dictCode = "code$i"
                dictValue = "value$i"
                dictTypeId = "type$i"
                sort = i
                buildIn = true
                createTime = LocalDateTime.now()
                creatorId = "user1"
                creatorOrgId = "org1"
            }
            sysDictMapper.insert(dict)
        }

        // 插入字典数据 - user2的数据
        for (i in 1..8) {
            val dict = SysDict().apply {
                dictId = "dict_user2_$i"
                dictName = "字典名称user2_$i"
                dictCode = "code_user2_$i"
                dictValue = "value_user2_$i"
                dictTypeId = "type2"
                sort = i + 100
                buildIn = true
                createTime = LocalDateTime.now()
                creatorId = "user2"
                creatorOrgId = "org2"
            }
            sysDictMapper.insert(dict)
        }

        // 插入user3的数据
        for (i in 1..5) {
            val dict = SysDict().apply {
                dictId = "dict_user3_$i"
                dictName = "字典名称user3_$i"
                dictCode = "code_user3_$i"
                dictValue = "value_user3_$i"
                dictTypeId = "type$i"
                sort = i + 200
                buildIn = true
                createTime = LocalDateTime.now()
                creatorId = "user3"
                creatorOrgId = "org1"
            }
            sysDictMapper.insert(dict)
        }

        logger.info("测试数据准备完成")
    }

    @Nested
    @DisplayName("分页一致性正确性验证")
    inner class PaginationConsistencyCorrectnessTest {

        @Test
        @DisplayName("单用户权限分页:验证count和分页SQL权限条件一致")
        fun testSingleUserPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 5)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1"))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 单用户权限分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id = 'user1'"), "Count SQL应包含单用户条件")
            assertTrue(paginationSql!!.contains("creator_id = 'user1'"), "分页SQL应包含单用户条件")
            assertTrue(paginationSql.contains("LIMIT"), "分页SQL应包含LIMIT")

            assertEquals(15, result.total)
            assertEquals(5, result.records.size)

            result.records.forEach { dict ->
                assertEquals("user1", dict.creatorId, "返回的数据应该都属于user1")
            }

            logger.info("单用户权限分页验证完成")
        }

        @Test
        @DisplayName("多用户权限分页:验证IN条件生成")
        fun testMultiUserPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 10)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1", "user2"))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 多用户权限分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id IN ('user1', 'user2')"), "Count SQL应包含IN条件")
            assertTrue(paginationSql!!.contains("creator_id IN ('user1', 'user2')"), "分页SQL应包含IN条件")

            assertEquals(23, result.total) // user1:15 + user2:8
            assertEquals(10, result.records.size)

            result.records.forEach { dict ->
                assertTrue(dict.creatorId in listOf("user1", "user2"), "返回的数据应该都属于user1或user2")
            }

            logger.info("多用户权限分页验证完成")
        }

        @Test
        @DisplayName("组织权限分页:验证组织ID权限注入")
        fun testOrgIdPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 10)

            val result = dataScopeContext.dataScope()
                .orgIdList(listOf("org1"))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 组织权限分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_org_id = 'org1'"), "Count SQL应包含组织权限条件")
            assertTrue(paginationSql!!.contains("creator_org_id = 'org1'"), "分页SQL应包含组织权限条件")

            assertEquals(20, result.total) // user1:15 + user3:5 (都是org1)
            assertEquals(10, result.records.size)

            result.records.forEach { dict ->
                assertEquals("org1", dict.creatorOrgId, "返回的数据应该都属于org1")
            }

            logger.info("组织权限分页验证完成")
        }

        @Test
        @DisplayName("用户+组织权限分页:验证OR条件组合")
        fun testUserAndOrgPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 15)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user2"))
                .orgIdList(listOf("org1"))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 用户+组织权限分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id = 'user2'") && countSql.contains("creator_org_id = 'org1'"),
                "Count SQL应包含用户和组织权限条件")
            assertTrue(paginationSql!!.contains("creator_id = 'user2'") && paginationSql.contains("creator_org_id = 'org1'"),
                "分页SQL应包含用户和组织权限条件")
            assertTrue(countSql.contains("OR"), "Count SQL应包含OR逻辑")
            assertTrue(paginationSql.contains("OR"), "分页SQL应包含OR逻辑")

            assertEquals(28, result.total) // user2:8 + org1:20
            assertEquals(15, result.records.size)

            result.records.forEach { dict ->
                assertTrue(dict.creatorId == "user2" || dict.creatorOrgId == "org1",
                    "返回的数据应该属于user2或org1")
            }

            logger.info("用户+组织权限分页验证完成")
        }
    }

    @Nested
    @DisplayName("特殊场景正确性验证")
    inner class SpecialScenarioCorrectnessTest {

        @Test
        @DisplayName("count为0场景:正确跳过分页查询")
        fun testCountZeroCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 10)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("nonexistent_user"))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== Count为0正确性验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("结果总数: {}", result.total)
            logger.info("结果记录: {}", result.records.size)

            assertNotNull(countSql, "Count SQL不应为空")
            assertTrue(countSql!!.contains("creator_id = 'nonexistent_user'"), "Count SQL应包含权限条件")

            assertEquals(0, result.total)
            assertEquals(0, result.records.size)

            if (paginationSql != null) {
                logger.info("分页SQL也被执行了: {}", paginationSql)
            } else {
                logger.info("count为0时正确跳过了分页查询")
            }

            logger.info("Count为0场景验证完成")
        }

        @Test
        @DisplayName("复杂JOIN分页查询:多表关联权限控制")
        fun testComplexJoinPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 5)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1"))
                .call {
                    sysDictMapper.selectDictWithType(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 复杂JOIN分页测试 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id = 'user1'"), "Count SQL应包含权限条件")
            assertTrue(paginationSql!!.contains("creator_id = 'user1'"), "分页SQL应包含权限条件")
            assertTrue(paginationSql.contains("LEFT JOIN"), "分页SQL应包含JOIN")
            assertTrue(paginationSql.contains("LIMIT"), "分页SQL应包含LIMIT")

            result.records.forEach { dict ->
                assertEquals("user1", dict.creatorId, "返回的数据应该都属于user1")
            }

            logger.info("复杂JOIN分页查询验证完成")
        }

        @Test
        @DisplayName("条件分页查询:WHERE条件与权限条件结合")
        fun testConditionalPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 10)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1", "user2"))
                .call {
                    sysDictMapper.selectByCondition(page, true, "字典名称", "type1")
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 条件分页查询验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id IN ('user1', 'user2')"), "Count SQL应包含权限条件")
            assertTrue(paginationSql!!.contains("creator_id IN ('user1', 'user2')"), "分页SQL应包含权限条件")
            assertTrue(paginationSql.contains("LIKE"), "分页SQL应包含业务查询条件")
            assertTrue(paginationSql.contains("dict_type_id ="), "分页SQL应包含业务查询条件")

            result.records.forEach { dict ->
                assertTrue(dict.creatorId in listOf("user1", "user2"), "返回的数据应该都属于user1或user2")
                assertTrue(dict.dictName!!.contains("字典名称"), "返回的数据应该包含查询条件")
                assertEquals("type1", dict.dictTypeId, "返回的数据应该都是type1类型")
            }

            logger.info("条件分页查询验证完成")
        }

        @Test
        @DisplayName("指定页码分页:验证非首页权限处理")
        fun testSpecificPagePaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(3, 5) // 第3页，每页5条

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1"))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 指定页码分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("当前页: {}", result.current)
            logger.info("每页大小: {}", result.size)
            logger.info("总记录数: {}", result.total)
            logger.info("总页数: {}", result.pages)
            logger.info("当前页记录数: {}", result.records.size)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id = 'user1'"), "Count SQL应包含权限条件")
            assertTrue(paginationSql!!.contains("creator_id = 'user1'"), "分页SQL应包含权限条件")
            assertTrue(paginationSql.contains("LIMIT"), "分页SQL应包含正确的LIMIT")

            result.records.forEach { dict ->
                assertEquals("user1", dict.creatorId, "返回的数据应该都属于user1")
            }

            logger.info("指定页码分页验证完成")
        }
    }

    @Nested
    @DisplayName("分页配置组合正确性验证")
    inner class PaginationConfigurationCorrectnessTest {

        @Test
        @DisplayName("表过滤+分页:验证表过滤在分页中的正确性")
        fun testTableFilterPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 10)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1"))
                .excludeTableNames(listOf("sys_user", "sys_role")) // 排除其他表，不影响sys_dict
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 表过滤分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id = 'user1'"), "表过滤不应影响目标表的权限条件")
            assertTrue(paginationSql!!.contains("creator_id = 'user1'"), "表过滤不应影响目标表的权限条件")

            assertEquals(15, result.total)
            assertEquals(10, result.records.size)

            result.records.forEach { dict ->
                assertEquals("user1", dict.creatorId, "返回的数据应该都属于user1")
            }

            logger.info("表过滤分页验证完成")
        }

        @Test
        @DisplayName("自定义列+分页:验证自定义列映射在分页中的正确性")
        fun testCustomColumnPaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 8)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user2"))
                .orgIdList(listOf("org2"))
                .scopedColumns(listOf(
                    dataScopeContext.dataScopeProvider.newUserIdColumn("creator_id", ""),
                    dataScopeContext.dataScopeProvider.newOrgIdColumn("creator_org_id", "")
                ))
                .call {
                    sysDictMapper.selectDictPage(page, true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 自定义列分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id = 'user2'") || countSql.contains("creator_org_id = 'org2'"),
                "Count SQL应使用自定义列名")
            assertTrue(paginationSql!!.contains("creator_id = 'user2'") || paginationSql.contains("creator_org_id = 'org2'"),
                "分页SQL应使用自定义列名")

            assertEquals(8, result.total)
            assertEquals(8, result.records.size)

            result.records.forEach { dict ->
                assertTrue(dict.creatorId == "user2" || dict.creatorOrgId == "org2",
                    "返回的数据应该属于user2或org2")
            }

            logger.info("自定义列分页验证完成")
        }

        @Test
        @DisplayName("指定表类型分页:验证特定类型字典的分页查询")
        fun testSpecificTypePaginationCorrectness() {
            testPaginationInterceptor.clearCapturedSqls()

            val page = Page<SysDict>(1, 10)

            val result = dataScopeContext.dataScope()
                .userIdList(listOf("user1", "user3"))
                .call {
                    sysDictMapper.selectByTypeId(page, "type1", true)
                }

            val countSql = testPaginationInterceptor.getLastCapturedCountSql()
            val paginationSql = testPaginationInterceptor.getLastCapturedSql()

            logger.info("=== 指定表类型分页验证 ===")
            logger.info("Count SQL: {}", countSql)
            logger.info("分页 SQL: {}", paginationSql)
            logger.info("查询结果数: {}", result.records.size)
            logger.info("总记录数: {}", result.total)

            assertNotNull(countSql, "Count SQL不应为空")
            assertNotNull(paginationSql, "分页SQL不应为空")

            assertTrue(countSql!!.contains("creator_id IN ('user1', 'user3')"), "Count SQL应包含权限条件")
            assertTrue(paginationSql!!.contains("creator_id IN ('user1', 'user3')"), "分页SQL应包含权限条件")
            assertTrue(paginationSql.contains("dict_type_id = "), "分页SQL应包含业务条件")

            result.records.forEach { dict ->
                assertTrue(dict.creatorId in listOf("user1", "user3"), "返回的数据应该都属于user1或user3")
                assertEquals("type1", dict.dictTypeId, "返回的数据应该都是type1类型")
            }

            logger.info("指定表类型分页验证完成")
        }
    }
}
